/**
 * Copyright (C) 2008+ Spark Zheng
 *
 * @file	include/except.h
 * @brief
 *      Exception Module (implement from prototype in book < C I&I >):
 *      Work as follow sentences:
 *
 *	do {
 *		create and push an except_frame;
 *
 *		if (first return from setjmp) {
 *			S;
 *		} else if (exception is e1) {
 *			S1;
 *		} else if (exception is e2) {
 *			S2;
 *			......
 *		} else if (exception is eN) {
 *			SN;
 *		} else {
 *			S0;
 *		}
 *
 *		if (an exception occurred and wasn't handled)
 *			RERAISE();
 *	} while (0);
 *
 *      TRY-EXCEPT  /  TRY-FINALLY
 *
 * @see		dlib
 * @module      dlib
 *
 * @history
 *      version 0.0.1   Spark Zheng init except module
 */

#ifndef _DLIB_EXCEPT_H
#define _DLIB_EXCEPT_H

#ifdef __cplusplus
extern "C" {
#endif

#include <assert.h>
#include <setjmp.h>

/**
 * except flags for:
 * try-except / try-finally
 */
typedef enum {
	EXCEPT_ENTERED = 0,
	EXCEPT_RAISED,
	EXCEPT_HANDLED,
	EXCEPT_FINALIZED,
	EXCEPT_FLAG_NUM
} except_flag_t;

typedef struct exception exception_t;
typedef struct except_frame except_frame_t;

struct exception {
	const int index;
	const char *msg;
};

struct except_frame {
	except_frame_t	*prev;
	jmp_buf		env;

	const char	*file;
	int		line;

	const exception_t	*exception;
};

extern except_frame_t *except_stack;
extern const exception_t assert_faild;
/**
 * except_raise
 */
extern void except_raise(const exception_t *e, const char *file, int line);

/**
 * Exception Macros
 */
#define EF_FILE	 __ef.file
#define EF_LINE	 __ef.line

#ifndef RAISE
#define RAISE(e)	except_raise(&(e), __FILE__, __LINE__)
#endif
#ifndef RERAISE
#define RERAISE(ef)	except_raise((ef).exception, (ef).file, (ef).line)
#endif

#define TRY \
	do { \
		volatile int __ef_flag = EXCEPT_ENTERED; \
		except_frame_t __ef; \
		__ef.prev = except_stack; \
		except_stack = &__ef; \
		__ef_flag = setjmp(__ef.env); \
		if (__ef_flag == EXCEPT_ENTERED) {

#define EXCEPT(e) \
			if (__ef_flag == EXCEPT_ENTERED) { \
				except_stack = except_stack->prev; \
			} \
		} else if (__ef.exception == &(e)) { \
			__ef_flag = EXCEPT_HANDLED;

#define ELSE \
			if (__ef_flag == EXCEPT_ENTERED) { \
				except_stack = except_stack->prev; \
			} \
		} else { \
			__ef_flag = EXCEPT_HANDLED;

#define FINALLY \
			if (__ef_flag == EXCEPT_ENTERED) { \
				except_stack = except_stack->prev; \
			} \
		} \
		{	if (__ef_flag == EXCEPT_ENTERED) { \
				__ef_flag = EXCEPT_FINALIZED; \
			}

#define END_TRY \
			if (__ef_flag == EXCEPT_ENTERED) { \
				except_stack = except_stack->prev; \
			} \
		} \
		if (__ef_flag == EXCEPT_RAISED) { \
			RERAISE(__ef); \
		} \
	} while (0);


/**
 * Assert redefine
 */
#ifdef assert
#undef assert
#ifdef _DEBUG
//extern void assert(int e);
#define assert(e)	do { if (!(e)) RAISE(assert_faild); } while (0)
#else
#define assert(e)
#endif
#endif

#ifdef __cplusplus
}
#endif
#endif
